/*
 * utils.js.
 */

/*--------------------- file -------------------*/
/** crete a new project. */
function newProject() {
    if (globalVar.projectId) {
        alert("A project has already been created, close it first.");
        return;
    }
    interval.total = 0;
    initProject();
}

/** close current project, clear all datas. */
function closeProject() {
    if (globalVar.projectId) {
        globalVar.ontoView.clear();
        globalVar.dbView.clear();
        clearArray(globalVar.ontos);
        clearArray(globalVar.dbs);
        clearArray(globalVar.mappingTables);
        OAT.Dom.clear(globalVar.canvas);
        OAT.Dom.hide(globalVar.canvas);
        globalVar.projectId = false;
    }
}

/**
 * parse prefix:name, and return object { name:name, uri:uri }.
 */
function parseCompactUri(str) {
    if (!str || str == "") {
        return;
    }
    var data = str.split(":");
    var uri = "";
    for (var prefix in prefixMap) {
        var o = prefixMap[prefix];
        if (o.shortcut && o.prefix) {
            if (o.shortcut == data[0]) {
                uri = o.prefix + data[1];
                break;
            }
        }
    }
    var ret = {
        name:data[1],
        uri:uri
    }
    return ret;
}

/**
 * 
 */
function parseJoinCondition(str) {
    if (!str || str == "") {
        return;
    }
    
}

/**
 * parse meta schema script file.
 */
function parseMetaSchema(data, callback) {
    if (typeof(data) != 'string') {
        return;
    }
    var content = data;
    var startPos = -1;
    var endPos = -1;
    // get all prefix
    startPos = content.indexOf("prefix ");
    while (startPos > -1) {
        endPos = content.indexOf(":", startPos);
        var shortcut = content.substring(startPos + 7, endPos);
        startPos = content.indexOf("<", endPos) + 1;
        endPos = content.indexOf(">", startPos);
        var prefix = content.substring(startPos, endPos);
        //alert(shortcut + ": " + prefix);
        var tmp = prefixMap[prefix];
        if ((typeof tmp) == 'undefined') {
            var data = {
                shortcut:shortcut,
                prefix:prefix
            };
            prefixMap[prefix] = data;
        }
        // next
        startPos = content.indexOf("prefix ", endPos);
    }
    // scan all iri class
    var iriClasses = {};
    startPos = content.indexOf("create iri class ");
    while (startPos > -1) {
        endPos = content.indexOf(":", startPos + 17);
        var prefix = content.substring(startPos + 17, endPos);
        startPos = endPos + 1;
        endPos = content.indexOf(" ", startPos);
        var name = content.substring(startPos, endPos);
        startPos = content.indexOf("\"", endPos);
        endPos = content.indexOf("\"", startPos + 1);
        var conversionRule = content.substring(startPos + 1, endPos);
        startPos = content.indexOf("(", endPos);
        endPos = content.indexOf(")", startPos);
        var argsStr = content.substring(startPos + 1, endPos);
        // alert("prefix: " + prefix + ", name: " + name + ", args: " + argsStr);
        var args = argsStr.split(",");
        var primaryKeys = [];
        for (var i = 0; i < args.length; i++) {
            var tmpHead = args[i].indexOf("in ") + 3;
            var tmpEnd = args[i].indexOf(" ", tmpHead);
            var column = args[i].substring(tmpHead, tmpEnd);
            tmpHead = tmpEnd + 1;
            tmpEnd = args[i].indexOf(" ", tmpHead);
            var dataType = args[i].substring(tmpHead, tmpEnd);
            var params = args[i].substring(tmpEnd + 1);
            // alert("column: " + column + ", dataType: " + dataType + ", params: " + params);
            primaryKeys.push([ column, dataType, params ]);
        }
        var data = {
            constIRI: false,
            name: prefix + ":" + name,
            conv:conversionRule,
            primaryKey:primaryKeys,
            str:""
        }
        iriClasses[data.name] = data;
        startPos = content.indexOf("create iri class ", endPos);
    }
    // mapping section
    startPos = content.indexOf("alter quad storage virtrdf:") + 27;
    endPos = content.indexOf(" ", startPos);
    if (endPos > content.indexOf("\n", startPos))  {
        endPos = content.indexOf("\n", startPos);
    }
    var storage = content.substring(startPos, endPos);
    var lastIndex = endPos;
    startPos = content.indexOf("from ");
    var tables = {};
    while (startPos > -1) {
        endPos = content.indexOf("as", startPos);
        var tableUri = content.substring(startPos + 5, endPos - 1);
        lastIndex = startPos;
        startPos = content.indexOf("from ", endPos);
        var tmpPos = startPos;
        if (startPos < endPos || startPos > content.indexOf("}", endPos)) {
            tmpPos = content.indexOf("{", endPos);
            startPos = -1;
        }
        var tableName = content.substring(endPos + 3, tmpPos - 1);
        // alert("table uri: " + tableUri + ", tableName: " + tableName);
        var data = {
            name:tableName,
            uri:tableUri
        };
        tables[tableName] = data;
    }
    startPos = content.indexOf("{", lastIndex);
    startPos = content.indexOf("create virtrdf:") + 15;
    endPos = content.indexOf(" ", startPos);
    var graph = content.substring(startPos, endPos);
    startPos = content.indexOf("(\"", endPos);
    endPos = content.indexOf("\")", startPos);
    var graphIRI = content.substring(startPos + 2, endPos);
    // alert("storage: " + storage + ", graph: " + graph + ", iri; " + graphIRI);
    saveOption.storage = storage;
    saveOption.graph = graph;
    saveOption.uri = graphIRI;
    startPos = content.indexOf("{", endPos);
    lastIndex = content.indexOf("}.", startPos);
    var sections = content.substring(startPos + 1, lastIndex);
    sections = sections.replace(/\n/g, "");
    sections = sections.replace(/\t/g, " ");
    var mps = sections.split(". ");
    for (var i = 0; i < mps.length; i++) {
        var section = mps[i];
        if (section.indexOf(":") == -1) {
            continue;
        }
        endPos = section.indexOf(":");
        var mappingTable = null;
        var newTable = null;
        var newOnto = null;
        var mappingOnto = null;
        while (endPos > -1) {
            startPos = section.lastIndexOf(" ", endPos) + 1;
            endPos = section.indexOf(" ", endPos);
            var iri = section.substring(startPos, endPos);
            startPos  = endPos + 1;
            endPos = section.indexOf(".", startPos);
            var table = section.substring(startPos, endPos);
            table = table.replace(/\(/, "");
            startPos = section.indexOf(" ", endPos);
            var column = section.substring(endPos + 1, startPos);
            column = column.replace(/\)/, "");
            endPos = section.indexOf(" a ", startPos);
            lastIndex = section.indexOf(" as ", startPos);
            if (endPos > -1 && endPos < lastIndex) {
                // ontology
                startPos = section.indexOf(":",  endPos + 3);
                startPos = section.indexOf(" ", startPos) ;
                var onto = section.substring(endPos + 3, startPos);
                var qn =  parseCompactUri(onto);
                newTable = new Table(tables[table].uri, table, statistic.table);
                statistic.table += 1;
                mappingTable = new MappingTable(newTable, statistic.mappingTable);
                statistic.mappingTable += 1;
                newOnto = new Ontology(qn.uri, qn.name);
                statistic.ontology += 1;
                mappingOnto = new MappingOnto(newOnto, statistic.mappingOnto);
                statistic.mappingOnto += 1;
                mappingOnto.iriClass = iriClasses[iri];
                mappingOnto.hasIRIClass = true;
                mappingTable.addMappingOnto(mappingOnto);
            } else {
                // property
                /*
                var mappingColumn = new MappingColumn(newColumn, statistic.mappingColumn);
                statistic.mappingColumn += 1;
                var newColumn = new Column(tables[table].uri + "." + column, column);
                statistic.column += 1;
                newTable.addColumn(newColumn);
                var mappingColumn = new MappingColumn(newColumn, statistic.mappingColumn);
                statistic.mappingColumn += 1;
                */
                var qn = parseCompactUri(iri);
                var newProp = new Property(qn.uri, qn.name);
                statistic.property += 1;
                startPos = section.indexOf("where ", startPos) ;
                if (startPos > -1 && startPos < lastIndex) {
                    // the property is a relationship, non-value
                    newProp.isValue = false;
                    var tmpIRI = table.substring(0, table.indexOf(" "));
                    mappingOnto.objectIRI = tmpIRI;
                    newProp.object = tmpIRI;
                    var join = section.substring(startPos, lastIndex - 1);
                    // TODO: join info
                } else {
                    // the property is a value node
                    var mappingProperty = new MappingProperty(newProp, statistic.mappingProperty);
                    statistic.mappingProperty += 1;
                    var newColumn = new Column(tables[table].uri + "." + column, column);
                    statistic.column += 1;
                    newTable.addColumn(newColumn);
                    var mappingColumn = new MappingColumn(newColumn, statistic.mappingColumn);
                    statistic.mappingColumn += 1;
                    mappingProperty.setMappingColumn(mappingColumn);
                    mappingOnto.addMappingProperty(mappingProperty);
                }
                newOnto.addProperty(newProp);
            }
            lastIndex = section.indexOf(";", lastIndex);
            if (lastIndex > -1) {
                endPos = section.indexOf(":", lastIndex);
            } else {
                endPos = -1;
            }
        }
        globalVar.ontos.push(newOnto);
        globalVar.mappingTables.push(mappingTable);
    }
    for (var i = 0; i < globalVar.mappingTables.length; i++) {
        var mt = globalVar.mappingTables[i];
        var objMt = null;
        for (var l = 0; l < mt.mappingOntos.length; l++) {
            if (mt.mappingOntos[l].objectIRI) {
                for (var j = 0; j < globalVar.mappingTables.length; j++) {
                    objMt = globalVar.mappingTables[j];
                    for (var k = 0; k < objMt.mappingOntos.length; k++) {
                        var props = mt.mappingOntos[l].ontology.properties;
                        for (var m = 0; m < props.length; m++) {
                            if (!props[m].isValue && props[m].object == objMt.mappingOntos[k].iriClass.name) {
                                var ontology = objMt.mappingOntos[k].ontology;
                                var re = new Relation(mt.mappingOntos[l], objMt.mappingOntos[k], globalVar.relationCount++);
                                props[m].object = ontology.uri;
                                re.propUri = props[m].uri;
                                if (mt.mappingOntos[l].equals(objMt.mappingOntos[k])) {
                                    mt.mappingOntos[l].addRelation(re);
                                } else {
                                    mt.mappingOntos[l].addRelation(re);
                                    objMt.mappingOntos[k].addRelation(re);
                                }
                                break;
                            }
                        }
                    }
                }
            }
        }
    }
    if (callback) {
        callback();
    }
}

/*--- helper functions used in save with meta schema ----*/
function verifyMapping() {
    var ret = true;
    var err = "";
    // to make sure the value is not null or empty
    if (saveOption.graph == "")  {
        err += "undefined graph name; ";
    }
    if (saveOption.uri  == "") {
        err += "undefined graph uri; ";
    }
    if (saveOption.storage == "") {
        err += "undefined storage name; ";
    }
    if (err != "") {
        alert(err);
        return false;
    }
    if (globalVar.mappingTables.length == 0) {
        alert("no table is mapped.");
        return false;
    }

    if (!globalVar.projectId || globalVar.mappingTables.length == 0)
        return false;
    for (var i = 0; i < globalVar.mappingTables.length; i++) {
        var mos = globalVar.mappingTables[i].mappingOntos;
        for (var j = 0; j < mos.length; j++) {
            var mps = mos[j].mappingProperties;
            for (var k = 0; k < mps.length; k++) {
                if (!mps[k].mappingColumn) {
                    ret = false;
                    break;
                }
            }
        }
    }
    return ret;
}

/** get a short name from complete uri. */
function getNameFromUri(uri) {
    if (uri === "undefined" || uri == "") {
        return;
    }
    var lastN = uri.lastIndexOf("#");
    var lastD = uri.lastIndexOf(".");
    var pos = lastN > lastD ? lastN + 1 : lastD + 1;
    var name = uri.substring(pos, uri.length);
    return name;
}

/** get prefix from complete uri. */
function getPrefixFromUri(uri) {
    if (uri === "undefined" || uri == "") {
        return;
    }
    var lastN = uri.lastIndexOf("#");
    var lastD = uri.lastIndexOf(".");
    var pos = lastN > lastD ? lastN + 1: lastD + 1;
    var prefix = uri.substring(0, pos);
    return prefix;
}

/** replace column's uri with ^{table_name.}^.columnName. */
function reformatColumnStr(uri) {
    var tables = [];
    var newUri = "";
    for (var i = 0; i < globalVar.mappingTables.length; i++) {
        tables.push(globalVar.mappingTables[i].table);
    }
    var tmp = getPrefixFromUri(uri);
    // omit last dot, so it wil be a table uri.
    var tbu = tmp.substring(0, tmp.length - 1);
    for (var i = 0; i < tables.length; i++) {
        if (tbu == tables[i].uri) {
            newUri = "^{" + tables[i].name + ".}^." + getNameFromUri(uri);
            break;
        }
    }
    return newUri;
}

/** change uri to another format(prefix:name). */
function reformatUri(uri) {
    var ret = "";
    var prefix = getPrefixFromUri(uri);
    var name = getNameFromUri(uri);
    var shortcut = prefixMap[prefix] ? prefixMap[prefix].shortcut : "";
    while (!shortcut || shortcut === "") {
        var shortcut = prompt("short name for " + prefix, "");
        var data = {
            shortcut:shortcut,
            prefix:prefix
        }
        prefixMap[prefix] = data;
    }
    ret = shortcut + ":" + name;
    return ret;
}

function convertJoinInfo(join) {
    var doc = "";
    var indent = "\t\t";
    if (join.left == "Join") {
        doc += convertJoinInfo(join.leftJoin);
    }
    var refs = join.refs;
    var condition = join.condition;
    var ops = condition.split("###");
    for (var i = 1; i < ops.length; i += 4) {
        var left = "";
        var operator = ops[i + 1];
        var right = "";
        for (var j = 0; j < refs.length; j++) {
            if (ops[i] == refs[j].id) {
                left = reformatColumnStr(refs[j].str);
            }
            if (ops[i + 2] == refs[j].id) {
                right = reformatColumnStr(refs[j].str);
            }
        }
        doc += indent + "where (" + left + operator + right + ")\n";
    }
    return doc;
}

function createIRIClass() {
   // create iri class
    var doc = "";
    for (var prop in prefixMap) {
        var prefix = prefixMap[prop];
        if (prefix.shortcut && prefix.prefix) { 
            doc += "prefix " + prefix.shortcut + ": <" + prefix.prefix + ">\n";
        }
    }
    for (var i = 0; i < globalVar.mappingTables.length; i++) {
        var mo = null;
        for (var j = 0; j < globalVar.mappingTables[i].mappingOntos.length; j++) {
            mo = globalVar.mappingTables[i].mappingOntos[j];
            if (mo.hasIRIClass) {
                var iriClass = mo.iriClass;
                if (iriClass.constIRI) {
                    // do nothing
                } else {
                    doc += "\ncreate iri class " + iriClass.name + " \"" + iriClass.conv + "\" (";
                    var keys = iriClass.primaryKey;
                    for (var k = 0; k < keys.length; k++) {
                        if (k > 0) 
                            doc += ", ";
                        doc += "in " + keys[k][0] +" " + keys[k][1] + " " + keys[k][2];
                    }
                    doc += ").\n";
                }
            }
        }
    }
    return doc;
}

/**
 * Scan mappings, and return data[0]: prifix, data[1]: iriClass and data[2]: mapping body.
 */
function parseMapping() {
    if (verifyMapping() == false) {
        return;
    }
    var body = "";
    var allPrefix = "";
    var iriClassContent = "";
    body = "alter quad storage virtrdf:" + saveOption.storage + "\n";
    for (var i = 0; i < globalVar.mappingTables.length; i++) {
        var table = globalVar.mappingTables[i].table;
        body += "from " + table.uri + " as " + table.name + '\n';
    }
    indent = "\t";
    body += "{\n" + indent + "create virtrdf:" + saveOption.graph + " as graph iri (\"" + saveOption.uri + '\") option (exclusive)\n\t{\n';
    for (var i = 0; i < globalVar.mappingTables.length; i++) {
        var table = globalVar.mappingTables[i].table;
        var mappingOnto;
        for (var h = 0; h < globalVar.mappingTables[i].mappingOntos.length; h++) {
            mappingOnto = globalVar.mappingTables[i].mappingOntos[h];
            if (!mappingOnto || mappingOnto.type != "MappingOnto") { // not mapping to a ontology, skip
                continue;
            }
            if (mappingOnto.hasIRIClass) {
                var iriClass = mappingOnto.iriClass;
                if (iriClass.constIRI) {
                    // do nothing
                } else {
                    iriClassContent += "create iri class " + iriClass.name + " \"" + iriClass.conv + "\" (";
                    var keys = iriClass.primaryKey;
                    for (var k = 0; k < keys.length; k++) {
                        if (k > 0) 
                            iriClassContent += ", ";
                        iriClassContent += "in " + keys[k][0] +" " + keys[k][1] + " " + keys[k][2];
                    }
                    iriClassContent += ").\n";
                }
            }
            var onto = mappingOnto.ontology;
            var tableIRI = "";
            if (!mappingOnto.hasIRIClass) {
                alert("table: " + mappingOnto.parent.table.name + "'s IRI not defined,");
                return;
            }
            var iriClass = mappingOnto.iriClass;
            if (iriClass.constIRI) {
                tableIRI = iriClass.str;
            } else {
                var keys = iriClass.primaryKey;
                tableIRI = iriClass.name + " (";
                for (var j = 0; j < keys.length; j++) {
                    if (j > 0) {
                        tableIRI += ", ";
                    }
                    tableIRI += table.name + "." + keys[j][0];
                }
                tableIRI += ")";
            }
            body += indent + tableIRI + " a " + reformatUri(onto.uri) + "\n";
            body += indent + indent + "as virtrdf:" + table.name + "-" + onto.name;
            var mProps = mappingOnto.mappingProperties;
            var prop = null;
            var column = null;
            // value node properties
            for (var j = 0; j < mProps.length; j++) {
                prop = mProps[j].property;
                column = mProps[j].mappingColumn.column;
                body += ";\n" + indent + reformatUri(prop.uri) + " " + table.name + "." + column.name;
                body += "\n" + indent + indent + "as virtrdf:" + table.name + "-" + column.name; 
            }
            // non value-node properties(relations)
            var relations = mappingOnto.relations;
            for (var j = 0; j < relations.length; j++) {
                if (relations[j].subject.parent.equals(globalVar.mappingTables[i]) && relations[j].join) {
                    var tableIRI = "";
                    if (!relations[j].object.hasIRIClass) {
                        alert("table: " + relations[j].object.parent.table.name + "'s IRI not defined,");
                        return;
                    }
                    var iriClass = relations[j].object.iriClass;
                    if (iriClass.constIRI) {
                        tableIRI = iriClass.str;
                    } else {
                        var keys = iriClass.primaryKey;
                        tableIRI = iriClass.name + " (";
                        for (var k = 0; k < keys.length; k++) {
                            if (k > 0) {
                                tableIRI += ", ";
                            }
                            tableIRI += relations[j].object.parent.table.name + "." + keys[k][0];
                        }
                        tableIRI += ")";
                    }
                    body += ";\n" + indent + reformatUri(relations[j].propUri) + " " + tableIRI + " "
                    body += "\n" + convertJoinInfo(relations[j].join);
                    body += indent + indent + "as virtrdf:" + table.name + "-" + getNameFromUri(relations[j].propUri);
                }
            }
            body += ".\n";
        }
    }
    body += indent + "}.\n" + "}." 
    // prefix
    for (var prop in prefixMap) {
        var prefix = prefixMap[prop];
        if (prefix.shortcut && prefix.prefix) { 
             allPrefix += "prefix " + prefix.shortcut + ": <" + prefix.prefix + ">\n";
        }
    }
    var doc = [ allPrefix, iriClassContent, body ];
    return doc;
}

/* ------------------- Meta Schema Language ------------------- */
function mslInfo() {
    if (verifyMapping() == false) {
        alert("Mapping is incomplete.");
        return;
    }
    var data = parseMapping();
    var doc = "use DB;\n\n";
    var indent = "";
    // drop the graph if exsit
    doc += "SPARQL \n" + data[0] + "drop quad map graph iri (\"" + saveOption.uri + "\").\n;\n\n";
    doc += "SPARQL \n" + data[0] + "drop quad map virtrdf:" + saveOption.storage + ".\n;\n\n";
    // meta schema mapping part
    doc += "SPARQL \n" + data[0] + data[1] + ";\n\n";
    doc += "SPARQL \n" + data[0] + data[2] + ";\n\n";
    // url rewriting
    // delete old rules (defined by earier user)
    doc += "delete from db.dba.url_rewrite_rule_list where urrl_list like \'linked_data_mapper_%\';\n";
    doc += "delete from db.dba.url_rewrite_rule where urr_rule like \'linked_data_mapper_%\';\n";
    // define each url rewriting rule
    for (var i = 0; i < urlRewriteRules.length; i++) {
        var rule = urlRewriteRules[i];
        doc += "DB.DBA.URLREWRITE_CREATE_REGEX_RULE (\n" +
            "\t\'" + rule.ruleIRI + "\',\n" +
            "\t" + rule.allowUpdate + ",\n" +
            "\t\'" +  rule.niceMatch + "\',\n" +
            "\t" + rule.niceParams + ",\n" +
            "\t" + rule.niceMinParams + ",\n" +
            "\t\'" + rule.targetCompose + "\',\n" +
            "\t" + rule.targetParams + ",\n" +
            "\t" + rule.targetExpn + ",\n" +
            "\t\'" + rule.acceptPattern + "\',\n" +
            "\t" + rule.doNotContinue + ",\n" +
            "\t" + rule.httpRedirectCodes + "\n);\n\n"
    }
    // add to rule list
    doc += "DB.DBA.URLREWRITE_CREATE_RULELIST (\n" +
        "\t\'linked_data_mapper_rule_list1\',\n" +
        "\t1,\n" +
        "\tvector (\n";
    for (var i = 0; i < urlRewriteRules.length; i++) {
        doc += "\t\t\'" + urlRewriteRules[i].ruleIRI + "\'";
        if (i < (urlRewriteRules.length - 1)) {
            doc += ",\n";
        } else {
            doc += "\n\t)\n);";
        }
    }
    return doc;
}

/* ------------------ DartGrid simple mapping file -------------------- */
/* parse the JoinInfo and generate XML structured string. */
function parseJoinInfo(join) {
    var ret = "<join"
    if (join.joinId)
        ret += " id=\"" + join.joinId + "\">\n<left";
    else
        ret += ">\n<left";
    if (join.left == "Join") {
        ret += ">\n" + parseJoinInfo(join.leftJoin) + "</left>\n";
    } else {
        ret += " id=\"" + join.subId + "\">" + join.left + "</left>\n";
    }
    ret += "<right id=\"" + join.objId + "\">" + join.right + "</right>\n";
    var refs = join.refs;
    ret += "<constraint>\n";
    for (var i = 0; i < refs.length; i++) {
        var id = refs[i].id;
        var father = refs[i].father;
        var str = refs[i].str;
        ret += "<column refid=\"" + id + "\" father=\"" + father + "\">" + str + "</column>\n";
    }
    ret += "<condition>" + join.condition + "</condition>\n";
    ret += "</constraint>\n</join>\n"
    return ret;
}

/** parse all mapping infos and generate XML structured string. */
function buildMappingInfoDoc() {
    if (verifyMapping() == false) {
        alert("The mapping is not complete.");
        return;
    }
    var xml="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<tableregister>\n";
    var joins = [];
    for (var i = 0; i < globalVar.mappingTables.length; i++) {
        var table = globalVar.mappingTables[i].table;
        var joinCount = 1;
        xml += "<table>\n<uri>" + table.uri + "</uri>\n";
        var mo = null;
        for ( var k = 0; k < globalVar.mappingTables[i].mappingOntos.length; k++) {
            mo = globalVar.mappingTables[i].mappingOntos[k];
            if (mo) {
                var onto = mo.ontology;
                xml += "<ontologies>\n<ontology isMainOnto=\"true\">\n<ontologyURI>";
                xml += onto.uri + "</ontologyURI>\n<ontoProps>\n";
                var mProps = mo.mappingProperties;
                var prop = null;
                for (var j = 0; j < mProps.length; j++) {
                    prop = mProps[j].property;
                    xml += "<ontoProp>\n<ontologyURI>" + prop.uri + "</ontologyURI>\n";
                    if (mProps[j].mappingColumn == null) {
                        alert("The property has no column set!");
                        return;
                    }
                    xml += "<column>" + mProps[j].mappingColumn.column.name + "</column>\n</ontoProp>\n";
                }
                var relations = globalVar.mappingTables[i].relations;
                for (var j = 0; j < relations.length; j++) {
                    if (relations[j].subject.parent.equals(globalVar.mappingTables[i]) && relations[j].join) {
                        var join = relations[j].join;
                        join.joinId = "joinin" + joinCount++;
                        xml += "<ontoProp>\n<ontologyURI>" + relations[j].propUri + "</ontologyURI>\n"
                        xml += "<table>" + relations[j].object.parent.table.uri + "</table>\n";
                        xml += "<joinref>" + join.joinId + "</joinref>\n";
                        xml += "</ontoProp>\n"
                        joins.push(join);
                    }
                }
                xml += "</ontoProps>\n</ontology>\n</ontologies>"
            } else {
                xml +="<ontologies />\n"
            }
            xml += "</table>\n"
        }
    }
    for (var i = 0; i < joins.length; i++) {
        xml += parseJoinInfo(joins[i]);
    }
    xml += "</tableregister>";
    return xml;
}

function generate() {
    if (verifyMapping() == false) {
        alert("The mapping is not complete.");
        return;
    }
    var callback = function(data) {
        // do nothing
    }
    var data = parseMapping();
    // drop existed quad storage and graph
    OAT.Xmla.query = "SPARQL " + data[0] + "drop quad map graph iri (\"" + saveOption.uri + "\").";
    OAT.Xmla.execute(callback);
    OAT.Xmla.query = "SPARQL " + data[0] + "drop quad map virtrdf:" + saveOption.storage + ".";
    OAT.Xmla.execute(callback);
    // create iri class
    OAT.Xmla.query = "SPARQL " + data[0] + data[1];
    OAT.Xmla.execute(callback);
    // define mapping
    OAT.Xmla.query = "SPARQL " + data[0] + data[2];
    OAT.Xmla.execute(callback);
    // delete exist rules
    OAT.Xmla.query = "delete from db.dba.url_rewrite_rule_list where urrl_list like \'linked_data_mapper_%\'";
    OAT.Xmla.execute(callback);
    OAT.Xmla.query = "delete from db.dba.url_rewrite_rule where urr_rule like \'linked_data_mapper_%\'";
    OAT.Xmla.execute(callback);
    // url rewriting rules
    for (var i = 0; i < urlRewriteRules.length; i++) {
        var rule = urlRewriteRules[i];
         OAT.Xmla.query = "DB.DBA.URLREWRITE_CREATE_REGEX_RULE (\n" +
            "\t\'" + rule.urleIRI + "\',\n" +
            "\t" + rule.allowUpdate + ",\n" +
            "\t\'" +  rule.niceMatch + "\',\n" +
             "\t" + rule.niceParams + ",\n" +
             "\t" + rule.niceMinParams + ",\n" +
             "\t\'" + rule.targetCompose + "\',\n" +
             "\t" + rule.targetParams + ",\n" +
             "\t" + rule.targetExpn + ",\n" +
             "\t\'" + rule.acceptPattern + "\',\n" +
             "\t" + rule.doNotContinue + ",\n" +
            "\t" + rule.httpRedirectCodes + "\n);"
        OAT.Xmla.execute(callback);
    }
    var doc = "DB.DBA.URLREWRITE_CREATE_RULELIST (\n" +
        "\t\'linked_data_mapper_rule_list1\',\n" +
        "\t1,\n" +
        "\tvector (\n";
    for (var i = 0; i < urlRewriteRules.length; i++) {
        doc += "\t\t\'" + urlRewriteRules[i].ruleIRI + "\'";
        if (i < (urlRewriteRules.length - 1)) {
            doc += ",\n";
        } else {
            doc += "\n\t)\n)";
        }
    }
    OAT.Xmla.query = doc;
    OAT.Xmla.execute(callback);
}

function closeDbView() {
    if (globalVar.dbView.show)
        globalVar.dbView.clear();
    globalVar.dbView.show = false;
}

function closeOntoView() {
    if (globalVar.ontoView.show)
        globalVar.ontoView.clear();
    globalVar.ontoView.show = false;
}

/*---------------------------- edit ----------------------------*/
function editUri() {
    if (globalVar.selectElement) {
        globalVar.selectElement.editUri();
    }
}

function editName() {
    if (globalVar.selectElement) {
        globalVar.selectElement.editName();
    }
}

function deleteElement() {
    if (globalVar.selectElement) {
        globalVar.selectElement.remove();
        globalVar.selectElement = false;
    }
}

/*------------------------- View ---------------------------- */
function queryWithiSPARQL() {
    var url = escape("/isparql");
    window.open(url);
}

/*-------------------------- Window ------------------------*/
function showDbres() {
    if (!globalVar.projectid)
        return;
    if (!globalVar.dbView.show)
        globalVar.dbView.addDb(globalVar.dbs);
    globalVar.dbView.show = true;
}

function showOnto() {
    if (!globalVar.projectId)
        return;
    if (!globalVar.ontoView.show)
        globalVar.ontoView.addOnto(globalVar.ontos);
    globalVar.ontoView.show = true;
}

/** clear a array. */
function clearArray(arr) {
    for (var i = 0; i < arr.length; i++) {
        delete arr[i];
    }
    arr.splice(0);
}


/** remove an custom element (Column, Table, MappingColumn and etc) from array. */
function deleteArrayElement(arr, elm) {
    for (var i = 0; i < arr.length; i++) {
        if (arr[i].equals(elm)) {
            arr.splice(i, 1);
            break;
        }
    }
}

/** pass additional arguments to setTimeout and setInterval. */
function bind(func, args) {
    return function() {
        func.apply(null, args);
    };
}

function showAllMappingUnits() {
    var data = OAT.Dom.getLT(globalVar.canvas);
    var leftTop = data;
    data = OAT.Dom.getWH(globalVar.canvas);
    var rightBottom = [ leftTop[0] + data[0], leftTop[1] + data[1] ];
    var nextX = leftTop[0] + 10;
    var nextY = leftTop[1] + 10;
    // alert(leftTop[0] + ", " + leftTop[1] + ", " + rightBottom[0] + ", " + rightBottom[1]);
    for (var i = 0; i < globalVar.mappingTables.length;i++) {
        var mt = globalVar.mappingTables[i];
        mt.draw(nextX, nextY);
        data = OAT.Dom.getWH(mt.content);
        if ((nextX + 2 * data[0]) <= rightBottom[0]) {
            nextX +=  data[0] + 30;
        } else {
            nextX = leftTop[0] + 30;
            nextY += data[1] + 30;
        }
    }
    // draw relations
    for (var i = 0; i < globalVar.mappingTables.length; i++) {
        for (var j = 0; j < globalVar.mappingTables[i].mappingOntos.length; j++) {
            globalVar.mappingTables[i].mappingOntos[j].updateRelations();
        }
    }
}
